package com.admin.domain.model;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by Jonsy
 * id   label   path        level order
 * 1    水果      0           1   1
 * 2    苹果      0,1         2   1
 * 3    梨子      0,1         2   2
 * 4    雪梨      0,1,3       3   1
 * 5    鸭梨      0,1,3       3   2
 */
public class TreeModel {

    private String id;

    private String label;

    private String path = "0";  //父节点的路径与父节点的id路径，用","分开，0表示父节点是根节点

    private int order = 1;  //排序

    private int type;//扩展字段。菜单类型，供不同业务区分

    private String style;//样式，方便ui展现

    /**
     * 状态 是否禁用
     */
    private boolean disabled;

    private List<? extends TreeModel> childNodes = new ArrayList<>();

    /**
     * FIXME 为当前节点设置新的子节点 - 错误。
     * 就是单纯的创建新节点！虽然有关系，但没有在bean上关联！
     * 应该改成静态的方法，使用参考对象来设置path信息！
     *
     * @param nodeId
     * @param label
     * @param order
     * @return
     */
    public TreeModel newChildNode(String nodeId, String label, int order){
        TreeModel node = new TreeModel();
        node.path = this.path + "," + this.id;
        node.id = nodeId;
        node.order = order;
        node.label = label;
        return node;
    }

    /***
     * 以level==1的节点作为开始节点构建树。
     *
     * @param nodes
     * @return
     */
    public static List<? extends TreeModel> buildTree(List<? extends TreeModel> nodes){
        if(isEmpty(nodes)){
            return null;
        }
        // 过滤
        List<? extends TreeModel> firstLevels = nodes.stream().filter(node -> !node.isDisabled() && node.getLevel() == 1).collect(Collectors.toList());
        // 排序
        sortByOrder(firstLevels);
        // 为每个节点设置子节点
        firstLevels.stream().forEach(node -> setChildren(node, nodes));
        return firstLevels;
    }

    /**
     * 为当前结点设置children。
     *
     * @param currentNode
     * @param nodeList
     */
    private static void setChildren(TreeModel currentNode, List<? extends TreeModel> nodeList){
        // 通过条件 node.path == currentNode.path+","+currentNode.id 来过滤currentNode的子节点
        List<? extends TreeModel> childrens = nodeList.stream().filter(node -> (!node.isDisabled() && node.getPath().equals(currentNode.getPath() + "," + currentNode.getId()))).collect(Collectors.toList());
        // 设置子节点
        currentNode.childNodes = childrens;
        // 结束条件
        if(isEmpty(childrens)){
            return;
        }
        // 排序
        sortByOrder(childrens);
        // 递归，继续为每个child节点设置子节点
        childrens.stream().forEach(node -> setChildren(node, nodeList));
    }

    /**
     * 给节点排序，基于node.order。
     *
     * @param firstLevels
     */
    private static void sortByOrder(List<? extends TreeModel> firstLevels){
//        firstLevels.sort((node1, node2) -> Integer.valueOf(node1.getOrder()).compareTo(Integer.valueOf(node2.getOrder())));
        firstLevels.sort(Comparator.comparing(node -> Integer.valueOf(node.getOrder())));
    }

    /***
     * 按树结构给节点排序。
     *
     * @param nodes
     */
    public static void sortByTree(List<? extends TreeModel> nodes){
        if(isEmpty(nodes)){
            return;
        }
        // 既然要以[node.path+","+node.id]排序，为啥还要这个排序？不懂啊
        sortByOrder(nodes);
//        nodes.sort((o1, o2) -> (o1.getPath() + "," + o1.getId()).compareTo(o2.getPath() + "," + o2.getId()));
        nodes.sort(Comparator.comparing(o -> (o.getPath() + "," + o.getId())));
    }

    private static boolean isEmpty(List nodes){
        return nodes == null || nodes.isEmpty();
    }

    /**
     * 按节点的path+id排序展示 - 不考虑children。
     * 应该修改，不应该带缩进，否则容易混淆。
     * TODO 其实就是直接输出集合中的对象，没有任何关联关系。
     *
     * @param nodes
     */
    private static void printTreeCollectionDirectly(List<TreeModel> nodes){
        if(isEmpty(nodes)){
            return;
        }

        sortByTree(nodes);

        nodes.stream().forEach(node -> {
            if(node.isDisabled()){
                return;
            }
            for(int i = 1; i < node.getLevel(); i++){
                System.out.print("\t");
            }
            System.out.println(node);
        });
    }

    /**
     * 以第一层为起点，递归方式展示父子层次树。
     * 展示完整的树。
     *
     * @param nodes
     */
    private static void printFirstLevelTreeToConsole(List<? extends TreeModel> nodes){
        if(isEmpty(nodes)){
            return;
        }
        nodes.forEach(item -> {
            if(item.isDisabled()){
                return;
            }
            for(int i = 1; i < item.getLevel(); i++){
                System.out.print("\t");
            }
            System.out.println(item);

            printFirstLevelTreeToConsole(item.getChildNodes());
        });
    }


    public static void main(String[] arg){
        //集合
        List<TreeModel> nodes = new ArrayList<>();
        //第一个节点对象
        TreeModel fruit = new TreeModel();
        fruit.setId("1");
        fruit.setLabel("水果");
        fruit.setOrder(2);
        nodes.add(fruit);

        //第二个节点对象
        TreeModel apple = fruit.newChildNode("7", "苹果", 2);
        nodes.add(apple);
        //第三个节点对象
        nodes.add(apple.newChildNode("4", "红富士", 2));
        //第四个节点对象
        nodes.add(apple.newChildNode("15", "山东苹果", 1));

        //第五个节点对象
        TreeModel lizi = fruit.newChildNode("e8", "梨子", 1);
        nodes.add(lizi);
        //第六个节点对象
        nodes.add(lizi.newChildNode("7r7", "雪梨", 1));
        //第七个节点对象
        nodes.add(lizi.newChildNode("t31o", "鸭梨", 2));

        //第八个节点对象
        TreeModel shucai = new TreeModel();
        shucai.setId("a101");
        shucai.setLabel("蔬菜");
        shucai.setOrder(1);
//        shucai.setDisabled(true); //TODO 注意这个啊，别被坑了
        nodes.add(shucai); //有啊，为什么按层次打印就没了？因为禁用了，我擦
        //第九个节点对象
        nodes.add(shucai.newChildNode("213", "白菜", 2));
        nodes.add(shucai.newChildNode("211", "花菜", 1)); //以order排序
        //直接打印 - 不考虑children，但以path排序。TODO 应该修改，不应该带缩进，否则容易混淆。
        printTreeCollectionDirectly(nodes);

        System.out.println("====================");
        //使用节点对象来构建树（以order排序）
        List<? extends TreeModel> tree = TreeModel.buildTree(nodes);
        //递归打印树
        printFirstLevelTreeToConsole(tree);
    }

    public String getId(){
        return id;
    }

    public void setId(String id){
        this.id = id;
    }

    public String getLabel(){
        return label;
    }

    public void setLabel(String label){
        this.label = label;
    }

    public String getPath(){
        return path;
    }

    public void setPath(String path){
        this.path = path;
    }

    public int getLevel(){
        if(path == null){
            return 1;
        }
        return path.split(",").length;
    }

    public int getOrder(){
        return order;
    }

    public void setOrder(int order){
        this.order = order;
    }

    public boolean isDisabled(){
        return disabled;
    }

    public void setDisabled(boolean disabled){
        this.disabled = disabled;
    }

    public String getStyle(){
        return style;
    }

    public void setStyle(String style){
        this.style = style;
    }

    public int getType(){
        return type;
    }

    public void setType(int type){
        this.type = type;
    }


    public List<? extends TreeModel> getChildNodes(){
        return childNodes;
    }

    @Override
    public String toString(){
        return label + "-" + path + "-" + id + "-" + order;
    }

    @Override
    public boolean equals(Object o){
        if(this == o){
            return true;
        }
        if(o == null || getClass() != o.getClass()){
            return false;
        }

        TreeModel treeModel = (TreeModel) o;

        return id != null ? id.equals(treeModel.id) : treeModel.id == null;

    }

    @Override
    public int hashCode(){
        return id != null ? id.hashCode() : 0;
    }

}
